// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#include <pollux/functions/lib/date_time_formatter_builder.h>
#include <pollux/functions/lib/date_time_formatter.h>

namespace kumo::pollux::functions {
    DateTimeFormatterBuilder::DateTimeFormatterBuilder(size_t literalBufSize) {
        literalBuf_ = std::unique_ptr<char[]>(new char[literalBufSize]);
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendEra() {
        tokens_.emplace_back(FormatPattern{DateTimeFormatSpecifier::ERA, 2});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendCenturyOfEra(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::CENTURY_OF_ERA, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendYearOfEra(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::YEAR_OF_ERA, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendWeekYear(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::WEEK_YEAR, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendWeekOfWeekYear(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::WEEK_OF_WEEK_YEAR, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendWeekOfMonth(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::WEEK_OF_MONTH, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendDayOfWeek0Based(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::DAY_OF_WEEK_0_BASED, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendDayOfWeek1Based(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::DAY_OF_WEEK_1_BASED, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendDayOfWeekText(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::DAY_OF_WEEK_TEXT, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendYear(
        size_t minDigits) {
        tokens_.emplace_back(FormatPattern{DateTimeFormatSpecifier::YEAR, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendDayOfYear(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::DAY_OF_YEAR, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendMonthOfYear(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::MONTH_OF_YEAR, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendMonthOfYearText(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::MONTH_OF_YEAR_TEXT, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendDayOfMonth(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::DAY_OF_MONTH, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendHalfDayOfDay() {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::HALFDAY_OF_DAY, 2});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendHourOfHalfDay(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::HOUR_OF_HALFDAY, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendClockHourOfHalfDay(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::CLOCK_HOUR_OF_HALFDAY, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendHourOfDay(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::HOUR_OF_DAY, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendClockHourOfDay(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::CLOCK_HOUR_OF_DAY, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendMinuteOfHour(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::MINUTE_OF_HOUR, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendSecondOfMinute(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::SECOND_OF_MINUTE, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendFractionOfSecond(
        size_t digits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::FRACTION_OF_SECOND, digits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendTimeZone(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::TIMEZONE, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendTimeZoneOffsetId(
        size_t minDigits) {
        tokens_.emplace_back(
            FormatPattern{DateTimeFormatSpecifier::TIMEZONE_OFFSET_ID, minDigits});
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendLiteral(
        const std::string_view &literal) {
        return appendLiteral(literal.data(), literal.size());
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::appendLiteral(
        const char *literalStart,
        size_t literalSize) {
        std::strncpy(literalBuf_.get() + bufEnd_, literalStart, literalSize);
        bufEnd_ += literalSize;
        if (!tokens_.empty() &&
            tokens_.back().type == DateTimeToken::Type::kLiteral) {
            // Extend the previous literal string view.
            auto &prev = tokens_.back().literal;
            tokens_.back().literal =
                    std::string_view(prev.data(), prev.size() + literalSize);
        } else {
            tokens_.emplace_back(std::string_view(
                literalBuf_.get() + bufEnd_ - literalSize, literalSize));
        }
        return *this;
    }

    DateTimeFormatterBuilder &DateTimeFormatterBuilder::set_type(
        DateTimeFormatterType type) {
        type_ = type;
        return *this;
    }

    std::shared_ptr<DateTimeFormatter> DateTimeFormatterBuilder::build() {
        POLLUX_CHECK_NE(type_, DateTimeFormatterType::UNKNOWN);
        return std::make_shared<DateTimeFormatter>(
            std::move(literalBuf_), bufEnd_, std::move(tokens_), type_);
    }
} // namespace kumo::pollux::functions
